
//long_packet_max is 16K byte.
//continuous reading and writing
`include "defines.v"

module ppp_proc
(
		//input
    rst,
    clk,
    snk_afull,
    
    vld,
    sop,
    eop,
    mod,
    err,
    data,
    
    //regfile
    fcs32_16,   //0 :crc32 1:crc16
    del_num,   //
    rx_dropfcs,

    //output
    fifo_rdy,
    vld_byte_num,
    pkg_pulse,	
		word_cnt_pulse,
    snk_vld,    
		snk_sop,
		snk_eop,  
		snk_mod,
		snk_data,
		snk_err
);
    
// *************************
// INPUTS and OUPUTS
// *************************
input       	rst;
input       	clk;
input       	snk_afull/* synthesis syn_keep = 1 */;
            	 
input       	vld;
input       	sop;
input       	eop;  
input[3:0]  	mod;
input[127:0]	data;
input[1:0]  	err;

input  				fcs32_16;
input[2:0]  	del_num;
input  				rx_dropfcs;
            	
output      	fifo_rdy; 
output[3:0]  	vld_byte_num;
output      	pkg_pulse;
output      	word_cnt_pulse;
output				snk_vld;    
output				snk_sop;
output				snk_eop;  
output				snk_mod;
output[127:0]	snk_data;
output				snk_err;
// *************************
// PARAMETER
// *************************

parameter      IDLE      =   2'b00,
               RD_DAT    =   2'b01,
               ADD_1CYC  =   2'b10;

// *************************
// INTERNAL SIGNALS
// *************************
reg[9:0]			pre_addr;
reg[9:0]			data_addr;
//reg[9:0]			data_addr_rst;
reg[9:0]			dcnt;
reg[9:0]			rd_cnt;
reg[9:0]			rd_addr;
reg[9:0]			ram_dcnt;

reg        		vld_d1;
reg        		sop_d1;
reg        		sop_de1;
reg        		eop_d1;
reg    			err_d1;
reg[4:0]   		mod_d1;
reg[255:0] 		din_d1;    
reg        		sop_delay;
reg        		eop_d2;
reg     			err_d2;
reg[127:0] 		din_d2;              
reg        		flag_sop;
reg        		flag_eop;
reg[3:0]   		flag_mod;
reg        		ram_vld;
reg        		ram_sop;
reg        		ram_eop;
reg[127:0] 		ram_din;
reg     			ahead;
reg        		ahead_d1;

reg[3:0]			fcs_del_num;
reg[4:0]			remain;
reg[4:0]			remain_d1;
reg        		start;
reg        		flag_del;

reg	[9:0]			pkt_cnt;
reg						fifo_rd;
reg						fifo_rd_d1;
reg						fifo_wr;

reg						snk_sop;
reg						snk_eop;  
reg	[3:0]			snk_mod;
reg	[127:0]		snk_data;
reg						snk_vld; 
reg						snk_vldT; 
reg						data_vld; 
reg      			fifo_rdy;
reg	[3:0]			vld_byte_num;

//wire
wire[2:0]			fcs_num;
wire[4:0]			remainT;
wire[4:0]			modT;   
wire					snk_sopT;
wire					snk_eopT;  
wire[3:0]			snk_modT;
wire[127:0]		snk_dataT;
wire					snk_err;
wire					empty;
wire					full;
wire[15:0]		pkg_len_out;
wire[10:0]		pkt_cntT;
wire					fifo_full;
wire					pkg_pulse;
wire					word_cnt_pulse;
// *************************
// CODE
// *************************
always @(posedge clk or `RST_EDGE rst)
begin            
    if(rst == `RST_VALUE)
        sop_de1 <= 1'h0;        
    else if(vld==1'h1)// && eop==1'h0 && sop==1'h1)
        sop_de1 <= sop;   
    //else if(vld==1'h1)
    //    sop_de1 <= 1'h0;        
end

//delay
always @(posedge clk or `RST_EDGE rst)
begin            
    if(rst == `RST_VALUE)
    begin
    		vld_d1 <= 1'h0;
    		sop_d1 <= 1'h0;
    		eop_d1 <= 1'h0;
    		err_d1 <= 1'h0;
    		eop_d2 <= 1'h0;
    		err_d2 <= 1'h0;
    		sop_delay <= 1'h0;
        fcs_del_num <= 4'h0;
        snk_vld <= 1'h0;
        fifo_rd_d1 <= 1'h0;
        ahead_d1 <= 1'h0;
    end 
    else// if(vld==1'h1)
    begin
    		vld_d1 <= vld;
    		sop_d1 <= sop;
    		eop_d1 <= eop;
    		sop_delay <= sop_de1;  
    		err_d1 <= ((err[0]&rx_dropfcs)|err[1]|(~fifo_rdy));
    		eop_d2 <= eop_d1;
    		err_d2 <= err_d1;
        fcs_del_num <= {1'h0,fcs_num} + {1'h0,del_num};
        snk_vld <= snk_vldT;
        fifo_rd_d1 <= fifo_rd;
        ahead_d1 <= ahead;
    end 
end

always @(posedge clk or `RST_EDGE rst)
begin            
    if(rst == `RST_VALUE)
        mod_d1 <= 5'h0;        
    else if(mod==4'h0)
        mod_d1 <= 5'h10;      
    else
        mod_d1 <= {1'h0,mod};    
end

always @(posedge clk or `RST_EDGE rst)
begin            
    if(rst == `RST_VALUE)
    	din_d1 <= 256'h0;
    else if(sop==1'h1)
    begin
    	case({{~remainT[4]},del_num[2:0]})
			4'h0:		din_d1[255:128]  <= data[127:0];
			4'h1:		din_d1[255:136] <= data[119:0];
			4'h2:		din_d1[255:144] <= data[111:0];
			4'h3:		din_d1[255:152] <= data[103:0];
			4'h4:		din_d1[255:160] <= data[95:0];
			4'h5:		din_d1[255:168] <= data[87:0];			
			4'h8:		din_d1[127:0] <= data[127:0];
			4'h9:		din_d1[127:8] <= data[119:0];
			4'ha:		din_d1[127:16] <= data[111:0];
			4'hb:		din_d1[127:24] <= data[103:0];
			4'hc:		din_d1[127:32] <= data[95:0];
			default:	din_d1[127:40] <= data[87:0];
			endcase
    end
    else if(vld==1'h1)
    begin
    	case(remain)
    	5'h00:		din_d1[255:128] <= data[127:0];
    	5'h01:		din_d1[247:120] <= data[127:0];
    	5'h02:		din_d1[239:112] <= data[127:0];
    	5'h03:		din_d1[231:104] <= data[127:0];
    	5'h04:		din_d1[223:96] <= data[127:0];
    	5'h05:		din_d1[215:88] <= data[127:0];
    	5'h06:		din_d1[207:80] <= data[127:0];
    	5'h07:		din_d1[199:72] <= data[127:0];
    	5'h08:		din_d1[191:64] <= data[127:0];
    	5'h09:		din_d1[183:56] <= data[127:0];
    	5'h0A:		din_d1[175:48] <= data[127:0];
    	5'h0B:		din_d1[167:40] <= data[127:0];
    	5'h0C:		din_d1[159:32] <= data[127:0];
    	5'h0D:		din_d1[151:24] <= data[127:0];
    	5'h0E:		din_d1[143:16] <= data[127:0];
    	5'h0F:		din_d1[135:8] <= data[127:0];
    	5'h10:		din_d1[127:0] <= data[127:0];
    	5'h11:		{din_d1[119:0],din_d1[255:248]} <= data[127:0];
    	5'h12:		{din_d1[111:0],din_d1[255:240]} <= data[127:0];
    	5'h13:		{din_d1[103:0],din_d1[255:232]} <= data[127:0];
    	5'h14:		{din_d1[95:0],din_d1[255:224]} <= data[127:0];
    	5'h15:		{din_d1[87:0],din_d1[255:216]} <= data[127:0];
    	5'h16:		{din_d1[79:0],din_d1[255:208]} <= data[127:0];
    	5'h17:		{din_d1[71:0],din_d1[255:200]} <= data[127:0];
    	5'h18:		{din_d1[63:0],din_d1[255:192]} <= data[127:0];
    	5'h19:		{din_d1[55:0],din_d1[255:184]} <= data[127:0];
    	5'h1A:		{din_d1[47:0],din_d1[255:176]} <= data[127:0];
    	5'h1B:		{din_d1[39:0],din_d1[255:168]} <= data[127:0];
    	5'h1C:		{din_d1[31:0],din_d1[255:160]} <= data[127:0];
    	5'h1D:		{din_d1[23:0],din_d1[255:152]} <= data[127:0];
    	5'h1E:		{din_d1[15:0],din_d1[255:144]} <= data[127:0];
    	default:	{din_d1[7:0],din_d1[255:136]} <= data[127:0];
    	endcase
    end
end

//remain
assign fcs_num = fcs32_16 ? {3'h2} : {3'h4};
assign remainT = (remain - 5'h1);
assign modT = (mod==4'h0) ? {5'h10} : {1'h0,mod};
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			remain <= 5'h0;
		else if(sop==1'h0 && eop==1'h0 && vld==1'h1)//data
			remain <= remain + 5'h10;
		else if(eop==1'h0 && sop==1'h1)//only sop
			remain <= {{~remainT[4]},4'h0} + 5'h10 - {2'h0,del_num};
		else if(eop==1'h1 && sop==1'h0)//only eop
			remain <= remain + {1'h0,modT} - {2'h0,fcs_num};
		else if(eop==1'h1 && sop==1'h1)// && modT>fcs_del_num)//sop_eop
			remain <= {{~remainT[4]},4'h0} + modT - {1'h0,fcs_del_num};//{2'h0,fcs_num} - {2'h0,del_num};
end

always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			remain_d1 <= 5'h0;
		else //if(vld_d1==1'h1)
			remain_d1 <= remain;
end

//start
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			start <= 1'h0;
		else
			start <= ~remainT[4];
end

//ram_din
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
    	ram_din <= 128'h0;
		else if(del_num>3'h0)// && (sop_delay==1'h1 || sop_d1==1'h0))//when second data come, din_d1->ram_din
		begin
			case(start)
			1'h0:				ram_din[127:0] <= din_d1[127:0];
    	default:		ram_din[127:0] <= din_d1[255:128];
    	endcase
		end
		else if(del_num==3'h0)
		begin
			case(start)
			1'h0:				ram_din[127:0] <= din_d1[255:128];
    	default:		ram_din[127:0] <= din_d1[127:0];
    	endcase
		end
end

//ram_vld
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			ram_vld <= 1'h0;
		else if(del_num>3'h0 && ahead_d1==1'h0 && eop_d2==1'h1)// && sop_d2==1'h0)	//when del_num>3'h0 and ahead_d1==0, generate the one clock vld.
			ram_vld <= 1'h1;
		else if(del_num>3'h0 && sop_d1==1'h1) //del sop// && eop_d1==1'h0
			ram_vld <= 1'h0;
		//else if(ahead_d1==1'h1)
		//	ram_vld <= 1'h0;
		else
			ram_vld <= vld_d1;
end

//data_vld
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			data_vld <= 1'h0;
		else if(del_num>3'h0 && ahead_d1==1'h0 && eop_d2==1'h1)// && sop_d2==1'h0)	//when del_num>3'h0 and ahead_d1==0, generate the one clock vld. 
			data_vld <= 1'h1;
		else if(del_num>3'h0 && sop_d1==1'h1)//del sop// && eop_d1==1'h0
			data_vld <= 1'h0;
		else if(del_num==3'h0 && vld_d1==1'h1 && ahead==1'h1)//del eop
			data_vld <= 1'h0;
		else
			data_vld <= vld_d1;
end

//ahead
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			ahead <=1'h0;
		else if(eop==1'h1 && sop==1'h0 && mod==fcs_del_num && mod>4'h0)
			ahead <=1'h1;
		else if(eop==1'h1 && sop==1'h0 && mod<fcs_del_num && mod>4'h0)
			ahead <=1'h1;
		else
			ahead <=1'h0;
end

//data_addr
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			data_addr <= 10'h0;
		else if(del_num>3'h0 && ram_vld==1'h1 && flag_eop==1'h1 && flag_sop==1'h1 && flag_del==1'h1)//to discard packet
			data_addr <= data_addr;
		else if(del_num>3'h0 && ram_vld==1'h1 && flag_eop==1'h1 && flag_del==1'h1)//to discard packet
			data_addr <= pre_addr;
		else if(del_num==3'h0 && ram_vld==1'h1 && flag_eop==1'h1 && err_d2==1'h1)//to discard packet
			data_addr <= pre_addr;
		else if(del_num==3'h0 && ram_vld==1'h0 && ahead==1'h1)//ahead of eop, there is one clock that vld is low between pre_eop data and eop data. 
			data_addr <= data_addr - 10'h1;// data_addr add a more 1;
		else if(del_num==3'h0 && vld_d1==1'h1 && ahead==1'h1)//ahead of eop, vld is high between pre_eop data and eop data.
			data_addr <= data_addr;// - 10'h1;
		else if(ram_vld==1'h1)
			data_addr <= data_addr + 10'h1;			
end

//flag_mod
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			flag_mod <= 4'h0;
		else if(del_num>3'h0 && ahead_d1==1'h0 && eop_d2==1'h1)
			flag_mod <= remain_d1[3:0];
		else 
			flag_mod <= remain[3:0];
end

//flag_eop
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			flag_eop <= 1'h0;//
		else if(del_num>3'h0 && ahead_d1==1'h0 && eop_d2==1'h1)// && sop_d2==1'h0)	//when del_num>3'h0 and ahead==0, generate the one eop.
			flag_eop <= 1'h1;
		else if(del_num>3'h0)// && ahead==1'h1 && eop_d1==1'h1)//
			flag_eop <= (eop_d1&ahead);
		else if(del_num==3'h0 && eop_d1==1'h1 && sop_d1==1'h1)
			flag_eop <= 1'h1;
		else if(del_num==3'h0)
			flag_eop <= eop_d1;	//
end

//flag_del
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			flag_del <= 1'h0;//
		else if(del_num>3'h0 && ahead_d1==1'h0 && eop_d2==1'h1 && err_d2==1'h1)//
			flag_del <= 1'h1;
		else if(del_num==3'h0 && eop_d1==1'h1 && sop_d1==1'h1 && err_d1==1'h1)
			flag_del <= 1'h1;
		else
			flag_del <= (eop_d1&ahead&err_d1);	//
end

//flag_sop
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			flag_sop <= 1'h0;
		//else if(sop_d1==1'h1 && eop_d1==1'h1)//when del_num>0, the sop signal hold on till the next data come.
		//	flag_sop <= 1'h1;
		else if(del_num>3'h0)//when del_num>0, the sop signal hold on till the next data come.
			flag_sop <= sop_delay;
		else if(ahead==1'h1)//sop_delay
			flag_sop <= sop_delay;
		else
			flag_sop <= sop_d1;
end

//pre_addr, to discard packet
always @(posedge clk or `RST_EDGE rst)
begin 
		if(rst == `RST_VALUE)
    		pre_addr <= 10'h0;
    //else if(del_num==3'h0 && flag_sop==1'h0 && flag_eop==1'h1 && ram_vld==1'h1 && err_d2==1'h0)//when normal and eop, data_addr add 1.
    //		pre_addr <= data_addr + 10'h1;
    //else if(del_num>3'h0 && flag_sop==1'h0 && flag_eop==1'h1 && ram_vld==1'h1 && flag_del==1'h0)
    //		pre_addr <= data_addr + 10'h1;
    else if(flag_eop==1'h1 && ram_vld==1'h1)
    		pre_addr <= data_addr + 10'h1;
    else if(flag_sop==1'h1 && ram_vld==1'h1)
    		pre_addr <= data_addr;// - 10'h1;
end

sdpram6x1k u_flag_ram(
	.clka			(clk			),
	.wea			(ram_vld	),
	.addra		(data_addr),
	.dina			({flag_sop,flag_eop,flag_mod}),
	.clkb			(clk			),
	.addrb		(rd_addr),
	.doutb		({snk_sopT,snk_eopT,snk_modT})
);
	
sdpram128x1k u_data_ram(
	.clka			(clk				),
	.wea			(data_vld		),
	.addra		(data_addr	),
	.dina			(ram_din		),
	.clkb			(clk				),
	.addrb		(rd_addr		),
	.doutb		(snk_dataT		)
);

//dcnt
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			dcnt <= 10'h1;
		else if(flag_sop==1'h1 && ram_vld==1'h1)//&& rd_en==1'h1
			dcnt <= 10'h1;
		else if(data_vld==1'h1 && dcnt<10'h3ff)
			dcnt <= dcnt + 10'h1;						
end

//fifo_wr
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			fifo_wr <= 1'h0;
		else if(del_num>3'h0 && flag_eop==1'h1 && ram_vld==1'h1 && flag_del==1'h0)//&& rd_en==1'h1
			fifo_wr <= 1'h1;
		else if(del_num==3'h0 && flag_eop==1'h1 && ram_vld==1'h1 && err_d2==1'h0)
			fifo_wr <= 1'h1;
		else
			fifo_wr <= 1'h0;						
end

//rd_cnt, pkg_len_out is on line
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
    		rd_cnt <= 10'h0;
    else if(fifo_rd==1'h1 && rd_cnt==10'h0)//fifo_rd_d1==1'h0 && 
    		rd_cnt <= pkg_len_out[9:0];// - 10'h1;
    else if(fifo_rd==1'h1 && rd_cnt>10'h0)
    		rd_cnt <= rd_cnt + pkg_len_out[9:0] - 10'h1;
    else if(rd_cnt>10'h0)
    		rd_cnt <= rd_cnt - 10'h1;
end

//ram_dcnt
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
    		ram_dcnt <= 10'h0;
    else if(fifo_wr==1'h1 && fifo_rd==1'h1)
    		ram_dcnt <= ram_dcnt - pkg_len_out[9:0] + dcnt;// - 10'h1;
    else if(fifo_wr==1'h1)
    		ram_dcnt <= dcnt[9:0] + ram_dcnt;// - 10'h1;
    else if(fifo_rd==1'h1)
    		ram_dcnt <= ram_dcnt - pkg_len_out[9:0];
end

//fifo_rd
always @(posedge clk or `RST_EDGE rst)
begin 
		if(rst == `RST_VALUE)
    		fifo_rd <= 1'h0;
    else if(fifo_rd==1'h0 && snk_afull==1'h0 && rd_cnt==10'h0 && pkt_cntT>11'h0 && empty==1'h0)//&& empty==1'h0
    		fifo_rd <= 1'h1;
    //else if(snk_afull==1'h0 && (rd_cnt==10'h1 || rd_cnt==10'h2) && pkt_cntT>10'h3)//fifo_rd_d1==1'h1 && 
    //		fifo_rd <= 1'h1;
    else if(snk_afull==1'h0 && rd_cnt<10'h2 && pkt_cntT>11'h5)//fifo_rd_d1==1'h1 && 
    		fifo_rd <= 1'h1;
    else
    		fifo_rd <= 1'h0;
end

syn_fifo16x1k	u_fifo_pkg_len(
	.clk(clk),
	.din({6'h0,dcnt}),
	.wr_en(fifo_wr),
	.rd_en(fifo_rd),
	.prog_full_thresh(10'h384),
	.dout(pkg_len_out),
	.full(full),
	.empty(empty),
	.data_count(pkt_cntT),
	.prog_full(fifo_full)
);

//read data
//rd_addr, when error, reset rd_addr
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			rd_addr <= 10'h0;//10'h3ff;
		//else if(fifo_wr==1'h1 && pkt_cntT==10'h0 && empty==1'h1)
		//	rd_addr <= data_addr_rst - dcnt + 10'h1;
		else if(rd_cnt>10'h0)// && snk_afull==1'h0)
			rd_addr <= rd_addr + 10'h1;
end

//snk_vldT
always @(posedge clk or `RST_EDGE rst)
begin
		if(rst == `RST_VALUE)
			snk_vldT <= 1'h0;
		else if(rd_cnt>10'h0)
			snk_vldT <= 1'h1;
		else
			snk_vldT <= 1'h0;
end

//fifo_rdy
always @(posedge clk or `RST_EDGE rst)
begin
		if((rst == `RST_VALUE))
			fifo_rdy <= 1'h0;
		else if(ram_dcnt>10'h180)
			fifo_rdy <= 1'h0;
		else
			fifo_rdy <= ~fifo_full;
end

//output
always @(posedge clk or `RST_EDGE rst)
begin
		if((rst == `RST_VALUE))
		begin
			snk_sop <= 1'h0;
			snk_eop <= 1'h0;
			snk_mod <= 4'h0;
			snk_data <= 128'h0;
		end
		else if(snk_vldT==1'h1)
		begin
			snk_sop <= snk_sopT;
			snk_eop <= snk_eopT;
			snk_mod <= snk_modT;
			snk_data <= snk_dataT;
		end
		else
		begin
			snk_sop <= 1'h0;
			snk_eop <= 1'h0;
			snk_mod <= 4'h0;
			snk_data <= 128'h0;
		end
end

//vld_byte_num
always @(posedge clk or `RST_EDGE rst)
begin
		if((rst == `RST_VALUE))
			vld_byte_num <= 4'h0;
		else if(snk_vldT==1'h1 && snk_eopT==1'h1)
			vld_byte_num <= snk_modT;
		else
			vld_byte_num <= 4'h0;
end

assign pkg_pulse = snk_eop;
assign word_cnt_pulse = snk_vld;
assign snk_err = 1'h0;

endmodule

